﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;

namespace ICodeShare.UI.Controls.Assists
{
    /// <summary>
    /// 文本附加属性
    /// </summary>
    public static class TextBoxAssist
    {
        #region 输入验证

        ///   1)在xaml中添加引用 xmlns:asts="http://schemas.extended.wpf.com/assists"
        ///   2)在TextBox元素上设置属性 <TextBox asts:TextBoxAssist.InvalidPattern="[^a-z0-9A-Z_]" />
        ///   3)如果需要在Text属性改变时立即更新绑定，设置<TextBox asts:TextBoxAssist.InvalidPattern="[^a-z0-9A-Z_]"  asts:TextBoxAssist.UpdateBindingWhenTextChanged="True" />
        /// 2.在C#中使用：
        ///   1)添加 using ICodeShare.UI.Controls.Assists
        ///   2)代码 textBox.SetInvalidPattern("[^a-z0-9A-Z_]");
        /// 3.常用正则表达式：
        ///   1) [^\d\.]        ——只能输入数字和小数点
        ///   2) [^\d]          ——只能输入数字
        ///   3) [^a-z0-9A-Z_]  ——只能输入数字、字母和下划线
        #region InvalidPattern 正则表达式

        public static string GetInvalidPattern(this TextBox textbox)
        {
            return (string)textbox.GetValue(InvalidPatternProperty);
        }

        public static void SetInvalidPattern(this TextBox textbox, string value)
        {
            textbox.SetValue(InvalidPatternProperty, value);
        }

        // Using a DependencyProperty as the backing store for InvalidPattern.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InvalidPatternProperty = DependencyProperty.RegisterAttached(
            "InvalidPattern",
            typeof(string),
            typeof(TextBoxAssist),
            new PropertyMetadata(string.Empty, OnInvalidPatternPropertyChanged));


        private static void OnInvalidPatternPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TextBox textBox = d as TextBox;
            string pattern = e.NewValue as string;

            if ((bool)textBox.GetValue(UpdateBindingWhenTextChangedProperty) == false)
            {
                if (string.IsNullOrEmpty(pattern))
                {
                    textBox.KeyUp -= new KeyEventHandler(textBox_KeyUp);
                    textBox.KeyDown -= new KeyEventHandler(textBox_KeyDown);
                }
                else
                {
                    textBox.KeyUp += new KeyEventHandler(textBox_KeyUp);
                    textBox.KeyDown += new KeyEventHandler(textBox_KeyDown);
                }
            }

        }

        #endregion

        #region UpdateBindingWhenTextChanged 当输入改变时是否更新绑定

        public static bool GetUpdateBindingWhenTextChanged(this TextBox textbox)
        {
            return (bool)textbox.GetValue(UpdateBindingWhenTextChangedProperty);
        }

        public static void SetUpdateBindingWhenTextChanged(this TextBox textbox, bool value)
        {
            textbox.SetValue(UpdateBindingWhenTextChangedProperty, value);
        }

        // Using a DependencyProperty as the backing store for UpdateBindingWhenTextChanged.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UpdateBindingWhenTextChangedProperty =
            DependencyProperty.RegisterAttached("UpdateBindingWhenTextChanged", typeof(bool), typeof(TextBoxAssist), new PropertyMetadata(false, OnUpdateBindingWhenTextChangedPropertyChanged));


        private static void OnUpdateBindingWhenTextChangedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TextBox textBox = d as TextBox;
            bool value = (bool)e.NewValue;
            if (string.IsNullOrEmpty(textBox.GetValue(InvalidPatternProperty) as string))
            {
                if (value)
                {
                    textBox.KeyUp += new KeyEventHandler(textBox_KeyUp);
                    textBox.KeyDown += new KeyEventHandler(textBox_KeyDown);
                }
                else
                {
                    textBox.KeyUp -= new KeyEventHandler(textBox_KeyUp);
                    textBox.KeyDown -= new KeyEventHandler(textBox_KeyDown);

                }
            }
        }


        #endregion

        #region OriCursorPosition 初始鼠标位置

        private static int GetOriCursorPosition(this TextBox textbox)
        {
            return (int)textbox.GetValue(OriCursorPositionProperty);
        }

        private static void SetOriCursorPosition(this TextBox textbox, int value)
        {
            textbox.SetValue(OriCursorPositionProperty, value);
        }

        // Using a DependencyProperty as the backing store for FilterParameters.  This enables animation, styling, binding, etc...
        private static readonly DependencyProperty OriCursorPositionProperty =
            DependencyProperty.RegisterAttached("OriCursorPosition", typeof(int), typeof(TextBoxAssist), new PropertyMetadata(0));

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// KeyUp时，将非法字符删掉，并将TextBox的SelectionStart值还原为KeyDown时的值
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void textBox_KeyUp(object sender, KeyEventArgs e)
        {
            TextBox textBox = sender as TextBox;

            Debug.Assert(textBox != null, "sender should be a non-null TextBox!");
            Debug.Assert(e != null, "e should not be null!");


            int position = (int)textBox.GetValue(OriCursorPositionProperty);
            string pattern = textBox.GetValue(InvalidPatternProperty) as string;
            if (!string.IsNullOrEmpty(pattern))
            {
                if (Regex.IsMatch(textBox.Text, pattern))
                {
                    textBox.Text = Regex.Replace(textBox.Text, pattern, "");
                    if (position > textBox.Text.Length)
                        textBox.SelectionStart = textBox.Text.Length;
                    else
                        textBox.SelectionStart = position;
                }
                else
                {
                    if ((bool)textBox.GetValue(UpdateBindingWhenTextChangedProperty))
                    {
                        BindingExpression be = textBox.GetBindingExpression(TextBox.TextProperty);
                        if (be != null)
                            be.UpdateSource();
                    }
                }
            }
            else
            {
                if ((bool)textBox.GetValue(UpdateBindingWhenTextChangedProperty))
                {
                    BindingExpression be = textBox.GetBindingExpression(TextBox.TextProperty);
                    if (be != null)
                        be.UpdateSource();
                }
            }
        }


        /// <summary>
        /// KeyDown时，记录TextBox的SelectionStart
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void textBox_KeyDown(object sender, KeyEventArgs e)
        {
            TextBox textBox = sender as TextBox;

            Debug.Assert(textBox != null, "sender should be a non-null TextBox!");
            Debug.Assert(e != null, "e should not be null!");

            textBox.SetValue(OriCursorPositionProperty, textBox.SelectionStart);
        }
        #endregion

        #region AutoMoveFocus 自动移除

        public static void SetAutoMoveFocus(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoMoveFocusProperty, value);
        }

        [AttachedPropertyBrowsableForType(typeof(TextBox))]
        public static bool GetAutoMoveFocus(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoMoveFocusProperty);
        }

        public static readonly DependencyProperty AutoMoveFocusProperty = DependencyProperty.RegisterAttached(
         "AutoMoveFocus",
         typeof(bool),
         typeof(TextBoxAssist),
         new FrameworkPropertyMetadata(false, OnAutoMoveFocusChanged));

        static void OnAutoMoveFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is TextBox)
            {
                var txtBox = d as TextBox;
                if (txtBox == null)
                    return;

                if ((bool)e.NewValue)
                {
                    txtBox.PreviewKeyDown += TextBoxPreviewKeyDown;
                    txtBox.TextChanged += TextBoxTextChanged;
                }
                else
                {
                    txtBox.PreviewKeyDown -= TextBoxPreviewKeyDown;
                    txtBox.TextChanged -= TextBoxTextChanged;
                }
            }
        }

        static void TextBoxPreviewKeyDown(object sender, KeyEventArgs e)
        {
            var txtBox = sender as TextBox;
            if (txtBox == null)
                return;

            if (GetAutoMoveFocus(txtBox))
            {
                if ((e.Key == Key.Left)
                  && ((Keyboard.Modifiers == ModifierKeys.None)
                    || (Keyboard.Modifiers == ModifierKeys.Control)))
                {

                    e.Handled = TextBoxMoveFocusLeft(txtBox);
                }

                if ((e.Key == Key.Right)
                  && ((Keyboard.Modifiers == ModifierKeys.None)
                    || (Keyboard.Modifiers == ModifierKeys.Control)))
                {

                    e.Handled = TextBoxMoveFocusRight(txtBox);
                }

                if (((e.Key == Key.Up) || (e.Key == Key.PageUp))
                  && ((Keyboard.Modifiers == ModifierKeys.None)
                    || (Keyboard.Modifiers == ModifierKeys.Control)))
                {
                    e.Handled = TextBoxMoveFocusUp(txtBox);
                }

                if (((e.Key == Key.Down) || (e.Key == Key.PageDown))
                 && ((Keyboard.Modifiers == ModifierKeys.None)
                   || (Keyboard.Modifiers == ModifierKeys.Control)))
                {
                    e.Handled = TextBoxMoveFocusDown(txtBox);
                }
            }
        }


        static void TextBoxTextChanged(object sender, TextChangedEventArgs e)
        {
            var txtBox = sender as TextBox;
            if (txtBox == null)
                return;

            if (GetAutoMoveFocus(txtBox))
            {
                if ((txtBox.Text.Length != 0) &&
               (txtBox.Text.Length == txtBox.MaxLength) &&
               (txtBox.CaretIndex == txtBox.MaxLength) &&
               txtBox.CanMoveFocus(FocusNavigationDirection.Right, true))
                {
                    FocusNavigationDirection direction = (txtBox.FlowDirection == FlowDirection.LeftToRight)
                      ? FocusNavigationDirection.Right
                      : FocusNavigationDirection.Left;

                    txtBox.MoveFocus(new TraversalRequest(direction));
                }
            }
        }

        private static bool TextBoxMoveFocusLeft(TextBox sender)
        {
            if (sender.FlowDirection == FlowDirection.LeftToRight)
            {
                //occurs only if the cursor is at the beginning of the text
                if ((sender.CaretIndex == 0) && (sender.SelectionLength == 0))
                {
                    if (ComponentCommands.MoveFocusBack.CanExecute(null, sender))
                    {
                        ComponentCommands.MoveFocusBack.Execute(null, sender);
                        return true;
                    }
                    else if (sender.CanMoveFocus(FocusNavigationDirection.Left, false))
                    {
                        sender.MoveFocus(new TraversalRequest(FocusNavigationDirection.Left));
                        return true;
                    }
                }
            }
            else
            {
                //occurs only if the cursor is at the end of the text
                if ((sender.CaretIndex == sender.Text.Length) && (sender.SelectionLength == 0))
                {
                    if (ComponentCommands.MoveFocusBack.CanExecute(null, sender))
                    {
                        ComponentCommands.MoveFocusBack.Execute(null, sender);
                        return true;
                    }
                    else if (sender.CanMoveFocus(FocusNavigationDirection.Left, false))
                    {
                        sender.MoveFocus(new TraversalRequest(FocusNavigationDirection.Left));
                        return true;
                    }
                }
            }

            return false;
        }

        private static bool TextBoxMoveFocusRight(TextBox sender)
        {
            if (sender.FlowDirection == FlowDirection.LeftToRight)
            {
                //occurs only if the cursor is at the beginning of the text
                if ((sender.CaretIndex == sender.Text.Length) && (sender.SelectionLength == 0))
                {
                    if (ComponentCommands.MoveFocusForward.CanExecute(null, sender))
                    {
                        ComponentCommands.MoveFocusForward.Execute(null, sender);
                        return true;
                    }
                    else if (sender.CanMoveFocus(FocusNavigationDirection.Right, false))
                    {
                        sender.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
                        return true;
                    }
                }
            }
            else
            {
                //occurs only if the cursor is at the end of the text
                if ((sender.CaretIndex == 0) && (sender.SelectionLength == 0))
                {
                    if (ComponentCommands.MoveFocusForward.CanExecute(null, sender))
                    {
                        ComponentCommands.MoveFocusForward.Execute(null, sender);
                        return true;
                    }
                    else if (sender.CanMoveFocus(FocusNavigationDirection.Right, false))
                    {
                        sender.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
                        return true;
                    }
                }
            }

            return false;
        }

        private static bool TextBoxMoveFocusUp(TextBox sender)
        {
            int lineNumber = sender.GetLineIndexFromCharacterIndex(sender.SelectionStart);

            //occurs only if the cursor is on the first line
            if (lineNumber == 0)
            {
                if (ComponentCommands.MoveFocusUp.CanExecute(null, sender))
                {
                    ComponentCommands.MoveFocusUp.Execute(null, sender);
                    return true;
                }
                else if (sender.CanMoveFocus(FocusNavigationDirection.Up, false))
                {
                    sender.MoveFocus(new TraversalRequest(FocusNavigationDirection.Up));
                    return true;
                }
            }

            return false;
        }

        private static bool TextBoxMoveFocusDown(TextBox sender)
        {
            int lineNumber = sender.GetLineIndexFromCharacterIndex(sender.SelectionStart);

            //occurs only if the cursor is on the first line
            if (lineNumber == (sender.LineCount - 1))
            {
                if (ComponentCommands.MoveFocusDown.CanExecute(null, sender))
                {
                    ComponentCommands.MoveFocusDown.Execute(null, sender);
                    return true;
                }
                else if (sender.CanMoveFocus(FocusNavigationDirection.Down, false))
                {
                    sender.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                    return true;
                }
            }

            return false;
        }

        private static bool CanMoveFocus(this UIElement element, FocusNavigationDirection direction, bool reachedMax)
        {
            QueryMoveFocusEventArgs e = new QueryMoveFocusEventArgs(direction, reachedMax);
            element.RaiseEvent(e);
            return e.CanMoveFocus;
        }
        #endregion

        #region QueryMoveFocus 自动选中判断

        public static void AddQueryMoveFocusHandler(DependencyObject obj, RoutedEventHandler value)
        {
            UIElement e = obj as UIElement;
            if (e != null)
            {
                e.AddHandler(QueryMoveFocusEvent, value);
            }
        }


        public static void RemoveQueryMoveFocusHandler(DependencyObject obj, RoutedEventHandler value)
        {
            UIElement e = obj as UIElement;
            if (e != null)
            {
                e.RemoveHandler(QueryMoveFocusEvent, value);
            }
        }

        public static readonly RoutedEvent QueryMoveFocusEvent = EventManager.RegisterRoutedEvent(
               "QueryMoveFocus",
               RoutingStrategy.Bubble,
               typeof(QueryMoveFocusEventHandler),
               typeof(TextBoxAssist));

        #endregion
    }
}
